package autojcode.controller;

import autojcode.config.FreeMarkerTemplateUtils;
import autojcode.domain.TableColumn;
import autojcode.domain.JcodeProperties;
import autojcode.domain.TemplateParameter;
import autojcode.enums.DataBaseEnum;
import autojcode.enums.DirectoryEnum;
import autojcode.exception.MyBizException;
import autojcode.utils.CamelHelper;
import autojcode.utils.FileHelper;
import freemarker.template.Template;

import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.Date;

import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

/**
 * 描述：代码自动生成
 */
@Service
public class CodeGenerateUtils {
    //private static final Logger logger = LoggerFactory.getLogger(CodeGenerateUtils.class);

    private JcodeProperties jcodeProperties;

    public List<String> getDbTest(JcodeProperties properties) throws Exception {
        try {
            jcodeProperties = properties;
            List<String> tableNames = new ArrayList<>();
            ResultSet resultSet = getDbTables("%");
            while (resultSet.next()) {
                tableNames.add(resultSet.getString("TABLE_NAME"));
            }
            return tableNames;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 数据库整体自动生成代码
     *
     * @param properties 配置参数
     * @return
     * @throws MyBizException
     */
    public String autoJcodeForDb(JcodeProperties properties) throws MyBizException {
        StringBuilder result = new StringBuilder();
        try {
            if (ObjectUtils.isEmpty(properties.getGroup()) || ObjectUtils.isEmpty(properties.getArtifact())
                    || ObjectUtils.isEmpty(properties.getDb())
            ) {
                throw new MyBizException(-1, "group或Artifact不存在");
            }
            jcodeProperties = properties;

            TemplateParameter parameter = null;

            if (ObjectUtils.isEmpty(jcodeProperties.getTableName())) {
                jcodeProperties.setTableName("%");
            }
            String[] tables = jcodeProperties.getTableName().split(",");
            result.append("【生成路径】" + jcodeProperties.getDiskPath() + "/" + jcodeProperties.getArtifact());

            for (String table : tables) {
                jcodeProperties.setTableName(table);
                ResultSet resultSet = getDbTables(jcodeProperties.getTableName());
                while (resultSet.next()) {
                    try {
                        parameter = new TemplateParameter();
                        parameter.setTableNameOriginal(resultSet.getString("TABLE_NAME"));
                        parameter.setTableDescription(resultSet.getString("REMARKS"));
                        parameter.setTableNameHump4Class(CamelHelper.underline2CamelClass(parameter.getTableNameOriginal()));
                        parameter.setTableNameHump(CamelHelper.underlineToCamel(parameter.getTableNameOriginal()));
                        generateTemplate(parameter);
                    } catch (Exception ex) {
                        result.append("\n【" + parameter.getTableNameOriginal() + "】生成失败，原因:" + ex.getMessage());
                    }
                }
            }
        } catch (Exception e) {
            throw new MyBizException(-1, e.getMessage());
        } finally {

        }
        return result.toString();
    }

    /**
     * 生成模板
     *
     * @param templateParameter
     * @throws Exception
     */
    private void generateTemplate(TemplateParameter templateParameter) throws Exception {
        try {

            //填充信息
            fillTemplateParameter(templateParameter);

            //生成Model文件
            templateParameter.setDirectoryEnum(DirectoryEnum.DOMAIN);
            generateFileByTemplate(templateParameter);

            //生成DTO文件
            templateParameter.setDirectoryEnum(DirectoryEnum.DOMAIN_DTO);
            generateFileByTemplate(templateParameter);

            //生成Controller层文件
            templateParameter.setDirectoryEnum(DirectoryEnum.CONTROLLER);
            generateFileByTemplate(templateParameter);
            //生成服务层接口文件
            templateParameter.setDirectoryEnum(DirectoryEnum.SERVICES);
            generateFileByTemplate(templateParameter);

            //生成服务实现层文件
            templateParameter.setDirectoryEnum(DirectoryEnum.SERVICES_IMPL);
            generateFileByTemplate(templateParameter);

            //生成Mapper文件
            templateParameter.setDirectoryEnum(DirectoryEnum.MAPPER);
            generateFileByTemplate(templateParameter);

            //生成MapperProvider文件
            templateParameter.setDirectoryEnum(DirectoryEnum.MAPPER_PROVIDER);
            generateFileByTemplate(templateParameter);

            //生成Gradle
            templateParameter.setTableNameHump4Class("");
            templateParameter.setDirectoryEnum(DirectoryEnum.GRADLE);
            generateFileByTemplate(templateParameter);

            //生成APPLICATION_CONFIG
            templateParameter.setDirectoryEnum(DirectoryEnum.APPLICATION_CONFIG);
            generateFileByTemplate(templateParameter);

            //APPLICATION_CONFIG_DEV
            templateParameter.setDirectoryEnum(DirectoryEnum.APPLICATION_CONFIG_DEV);
            generateFileByTemplate(templateParameter);

            //APPLICATION_CONFIG_PROD
            templateParameter.setDirectoryEnum(DirectoryEnum.APPLICATION_CONFIG_PROD);
            generateFileByTemplate(templateParameter);

            //拷贝APPLICATION_CONFIG
            templateParameter.setDirectoryEnum(DirectoryEnum.COMMON);
            FileHelper.copyDirAndReplaceConent(new File("").getAbsolutePath() + "\\src\\main\\resources\\JavaTemplate\\com\\",
                    templateParameter.getProjectDirctory() + String.format(templateParameter.getDirectoryEnum().getPath(), templateParameter.getPackageName().replace('.', '/')),
                    "#{packageName}", templateParameter.getPackageName());


            templateParameter.setDirectoryEnum(DirectoryEnum.RESOURCES);
            FileHelper.copyDirAndReplaceConent(new File("").getAbsolutePath() + "\\src\\main\\resources\\JavaTemplate\\resource\\",
                    templateParameter.getProjectDirctory() + String.format(templateParameter.getDirectoryEnum().getPath(), templateParameter.getPackageName().replace('.', '/')),
                    "#{packageName}", templateParameter.getPackageName());

        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * 填充模板参数
     *
     * @param templateParameter 模板参数
     * @return
     * @throws Exception
     */
    private TemplateParameter fillTemplateParameter(TemplateParameter templateParameter) throws Exception {
        //获取表结构
        ResultSet resultSet = getTablesColumns(templateParameter.getTableNameOriginal());
        templateParameter.setProjectDirctory(jcodeProperties.getDiskPath() + "/" + jcodeProperties.getArtifact() + "/");
        String url = String.format(DataBaseEnum.MYSQL.getUrl(), jcodeProperties.getDb().getIp(), jcodeProperties.getDb().getDataBaseName());
        templateParameter.setDbUrl(url);
        templateParameter.setDbUserName(jcodeProperties.getDb().getUser());
        templateParameter.setDbPassword(jcodeProperties.getDb().getPassword());
        templateParameter.setAuthor(jcodeProperties.getAuthor());
        templateParameter.setPackageName(jcodeProperties.getGroup() + "." + jcodeProperties.getArtifact().replace("-", ""));
        templateParameter.setGenerateDate((new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()));
        templateParameter.setVersion(jcodeProperties.getVersion());
        //字段集合
        List<TableColumn> tableColumnList = new ArrayList<>();
        //字段
        TableColumn tableColumn = null;
        //依赖
        // List<String> importRely = new ArrayList<>();
        Set<String> importRely = new HashSet();
        while (resultSet.next()) {
            //表名称
            templateParameter.setTableNameOriginal(resultSet.getString("TABLE_NAME"));
            //id字段略过
            //if (resultSet.getString("COLUMN_NAME").equals("id")) continue;
            tableColumn = new TableColumn();
            //字段名称
            tableColumn.setColumnName(resultSet.getString("COLUMN_NAME"));
            //字段类型
            tableColumn.setColumnType(dbType2JavaType(resultSet.getString("TYPE_NAME")));
            //引入依赖
            String rely = getImportRely(tableColumn.getColumnType());
            if (!ObjectUtils.isEmpty(rely)) {
                importRely.add(rely);
            }
            //转换字段名称，如 sys_name 变成 SysName
            tableColumn.setColumnNameHumpUpper(CamelHelper.underline2CamelClass(resultSet.getString("COLUMN_NAME")));

            tableColumn.setColumnNameHump(CamelHelper.underlineToCamel(resultSet.getString("COLUMN_NAME")));
            //在数据库的注释
            tableColumn.setColumnComment(resultSet.getString("REMARKS"));
            tableColumnList.add(tableColumn);
        }
        templateParameter.setImportRelyList(importRely);
        templateParameter.setTableNameHump4Class(CamelHelper.underline2CamelClass(templateParameter.getTableNameOriginal()));
        templateParameter.setTableNameHump(CamelHelper.underlineToCamel(templateParameter.getTableNameOriginal()));
        templateParameter.setTableColumnList(tableColumnList);
        return templateParameter;
    }

    /**
     * 生成模板文件
     *
     * @param templateParameter
     * @throws Exception
     */
    private void generateFileByTemplate(TemplateParameter templateParameter) throws Exception {
        Template template = FreeMarkerTemplateUtils.getTemplate(templateParameter.getDirectoryEnum().getTemplate());
        String path = templateParameter.getProjectDirctory() + String.format(templateParameter.getDirectoryEnum().getPath(), templateParameter.getPackageName().replace('.', '/'));
        File file = new File(path);
        if (!file.isDirectory()) {
            file.mkdirs();
        }
        String makeFilePath = path + DirectoryEnum.GetFileName(templateParameter.getTableNameHump4Class(), templateParameter.getDirectoryEnum());
        file = new File(makeFilePath);
        FileOutputStream fos = new FileOutputStream(file);
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
        template.process(templateParameter, out);
        out.close();
        fos.close();
    }

    /**
     * 数据库字段转java字段类型
     *
     * @param dbType 数据库字段类型
     * @return
     */
    private String dbType2JavaType(String dbType) {
        String result = "";
        switch (dbType.toUpperCase()) {
            case "VARCHAR":
            case "CHAR":
            case "TEXT":
                result = "String";
                break;
            case "BLOB":
                result = "Byte[]";
                break;
            case "INTEGER":
                result = "Long";
                break;
            case "TINYINT":
            case "SMALLINT":
            case "MEDIUMINT":
                result = "Integer";
                break;
            case "BIT":
                result = "Boolean";
                break;
            case "BIGINT":
                result = "BigInteger";
                break;
            case "FLOAT":
                result = "Float";
                break;
            case "DOUBLE":
                result = "Double";
                break;
            case "DECIMAL":
                result = "BigDecimal";
                break;
            case "DATE":
                result = "Date";
                break;
            case "TIME":
                result = "Time";
                break;
            case "TIMESTAMP":
                result = "Timestamp";
                break;
            default:
                result = "String";
        }
        return result;
    }

    /**
     * 根据Java类型返回引入
     *
     * @param javaType Java字段类型
     * @return
     */
    private String getImportRely(String javaType) {
        String result = "";
        switch (javaType.toUpperCase()) {
            case "BIGDECIMAL":
                result = "java.math.BigDecimal";
                break;
            case "DATE":
                result = "java.util.Date";
                break;
            case "TIME":
                result = "java.sql.Time";
                break;
            case "TIMESTAMP":
                result = "java.sql.Timestamp";
                break;
            default:
                result = "";
        }
        return result;
    }


    /**
     * 数据库链接
     *
     * @return
     * @throws Exception
     */
    private Connection connection() throws MyBizException {
        try {
            Class.forName(DataBaseEnum.MYSQL.getDriver());
            String url = String.format(DataBaseEnum.MYSQL.getUrl(), jcodeProperties.getDb().getIp(), jcodeProperties.getDb().getDataBaseName()) + "&useInformationSchema=true&remarks=true";
            Connection connection = DriverManager.getConnection(url, jcodeProperties.getDb().getUser(), jcodeProperties.getDb().getPassword());
            return connection;
        } catch (Exception ex) {
            throw new MyBizException(-1, "数据库连接失败");
        }
    }

    /**
     * 获取表结构
     *
     * @param tableName
     * @return
     * @throws Exception
     */
    private ResultSet getTablesColumns(String tableName) throws Exception {
        DatabaseMetaData databaseMetaData = connection().getMetaData();
        ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");
        return resultSet;
    }

    /**
     * 获取数据库所有表
     *
     * @return
     * @throws Exception
     */
    private ResultSet getDbTables(String tableName) throws Exception {
        Connection connection = connection();
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        ResultSet resultSet = databaseMetaData.getTables(null, null, tableName, new String[]{"TABLE"});
        return resultSet;
    }
}
